/*
    线程安全：多个线程并发访问一段代码时，不会出现不同的结果，就叫做线程安全。访问全局变量等导致其他线程出问题，这就叫线程不安全。
    重入：同一个函数被多个线程调用，当一个线程还没有执行完，就有其他的线程再次调用，我们称之为重入。一个函数在重入的情况下，运行结果不会出现任何不同或者任何问题，叫做可重入函数。
    线程安全是描述多线程的。。 重入是描述函数。

    在多线程访问，如果函数不可重入，可能会出现线程不安全的。
    在多线程访问，如果函数可重入，一定不会出现线程安全的。
    如果一个函数中有全局变量，那么这个函数既不是线程安全也不是可重入的。

    死锁：持有自己的资源，申请别人的资源。
    死锁的四个必要条件：
        1、互斥条件：一个资源每次只能被一个执行流使用。
        2、请求与保持条件：一个执行流因请求资源而阻塞时，对已获得的资源保持不放。
        3、不剥夺条件：一个执行流已获得的资源，在未使用完之前，不能强行剥夺。
        4、循环等待条件：若干执行流之间形成一种头尾相接的循环等待资源的关系。（环路）

    解决死锁问题：
        1、破坏4个必要条件-破坏一个即可
        2、加锁顺序一致
        3、避免锁未释放的场景
        4、资源一次性分配
*/
#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include "LockGuard.hpp"

using namespace std;

#define NUM 4

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

int tickets = 5000; // 多线程模拟一轮抢票

class threadData
{
public:
    // threadData(int number, pthread_mutex_t* mutex)
    threadData(int number)
    {
        _thread_name = "thread-" + to_string(number);
        // _lock = mutex;
    }

    string _thread_name;

    // 有了全局的锁就不要这把锁了
    // pthread_mutex_t* _lock;
};

void *getTicket(void *args)
{
    threadData *td = static_cast<threadData *>(args);
    const char *name = td->_thread_name.c_str();
    while (true)
    {
        {
            LockGuard lockguard(&lock); // 临时的LockGuard对象，RAII风格的锁！
            if (tickets > 0)
            {
                usleep(1000);
                printf("who=%s, get a ticket: %d\n", name, tickets);
                tickets--;
            }
            else
            {
                break;
            }
        }
        usleep(15);
    }
    printf("%s...quit\n", name);
    return nullptr;
}

int main()
{
    // pthread_mutex_t lock;
    // pthread_mutex_init(&lock,nullptr);

    vector<pthread_t> tids;
    vector<threadData *> thread_datas;
    for (int i = 1; i <= NUM; ++i)
    {
        pthread_t tid;
        // threadData* td = new threadData(i, &lock);
        threadData *td = new threadData(i);
        thread_datas.push_back(td);
        pthread_create(&tid, nullptr, getTicket, thread_datas[i - 1]);
        tids.push_back(tid);
    }

    for (auto thread : tids)
    {
        pthread_join(thread, nullptr);
    }

    for (auto td : thread_datas)
    {
        delete td;
    }

    // pthread_mutex_destroy(&lock);
    return 0;
}


